class Compiler {
  constructor(vm){
    this.el = vm.$el
    this.vm = vm
    this.compile(this.el)
  }
  //模板编译，处理文本节点和元素节点
  compile(el){
     let childNodes = el.childNodes  //这里循环输出div id='app'
     Array.from(childNodes).forEach(node => {
      //处理文本节点 
      if(this.isTextNode(node)){
         this.compileText(node)
       }else if(this.isElementNode(node)){
         //处理元素节点
         this.compileElement(node)
       }

       //判断node节点，是否有子节点，如果有子节点，要递归调用compile
       if(node.childNodes && node.childNodes.length) {
         this.compile(node)
       }
     })
  }
  //编译元素节点，处理指令
  compileElement(node){
    //console.log(node.attributes)
    //遍历所有的属性节点
    Array.from(node.attributes).forEach(attr => {
       //判断是否是指令
       let attrName = attr.name
       //v-on:click-->click
       if(this.isMethods(attrName)){
        attrName = attrName.substr(5)
        let functionName = attr.value
        this.onMethods(node, functionName, attrName)
       }else if(this.isDirective(attrName)){
         //v-text --> text
         attrName = attrName.substr(2)
         let key = attr.value 
         this.update(node,key,attrName)
        }
    })
  
  }

  onMethods(node, functionName, attrName) {

      node.addEventListener(attrName, this.vm[functionName])
    
  }


  update(node,key,attrName){
    let updateFn = this[attrName + 'Updater']

    updateFn && updateFn.call(this, node, this.vm[key], key)//改变this指向，第一个参数this指向compiler，  // textUpdater,modelUpdater 
  }

   //处理v-html 指令 这里是一个div等标签包括的值
   htmlUpdater(node,value, key){
    node.innerHTML = value 
    new Watcher(this.vm, key ,(newValue) => {
      node.innerHTML = newValue
    })
  }

  //处理v-text 指令 这里是一个div等标签包括的值
  textUpdater(node,value, key){
    node.textContent = value 
    new Watcher(this.vm, key ,(newValue) => {
      node.textContent = newValue
    })
  }

  //v-model 处理的是input 里的值 所有是value
  modelUpdater(node,value, key){
    node.value = value   
    new Watcher(this.vm, key ,(newValue) => {
      node.value = newValue
    })
    //双向绑定
    node.addEventListener('input', ()=> {
      this.vm[key] = node.value
    })
  }

   //v-model 处理的是input 里的值 所有是value
   onUpdater(node,value, key){
    node.value = value   
    new Watcher(this.vm, key ,(newValue) => {
      node.value = newValue
    })
    //双向绑定
    node.addEventListener('value', ()=> {
      this.vm[key] = node.value
    })
  }

  //编译文本节点，处理差值表达式
  compileText(node){
   //  console.log(node)
   // 以对象的形式打印出来
    // console.dir(node)
    //{{ msg }} 正则表达式的的小括号有分组的功能
    let reg = /\{\{ (.+?)\}\}/
    let value = node.textContent
    if(reg.test(value)){
      let key = RegExp.$1.trim() //regexp 正则的构造函数，$1第一个小阔号的内容，使用trim去掉空格
      node.textContent = value.replace(reg, this.vm[key]) //{{ msg }} 将类似这样的格式变成new vue实例里的data 数据
    
    //创建watcher对象,当数据改变更新视图
    new Watcher(this.vm,key,(newValue) => {
      node.textContent = newValue
    })

    }

  }

  //判断元素属性是否是指令
  isDirective(attrName) {
    return attrName.startsWith('v-')
  }

  isMethods(attrName) {
    return attrName.startsWith('v-on:')
  }
  //判断节点是否是文本节点
  isTextNode(node){
    return node.nodeType === 3
  }
  //判断节点是否是元素节点
  isElementNode(node){
     return node.nodeType === 1
  }
}